package main;

import java.io.BufferedReader;
import java.io.FileReader;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.*;

import Automaton.*;
import Patterns.PatternAnalyser;
import Regex.*;

public class TestrunManager 
{
	//In this class the test is executed
	private MembershipSolver membershipSolver;
	private RegexSolver regexSolver;
	private BufferedReader reader;
	private PrintWriter writer;
	private ArrayList<String> patternStrings;
	private ArrayList<String> words;
	private String inputFilename;
	private String outputFilename;

	public TestrunManager(String inputFilename_, String outputFilename_) 
	{
		//set filenames
		inputFilename = inputFilename_;
		outputFilename = outputFilename_;
	}

	public void run() 
	{
		//read the input file containing all instances to solve
		readInput();

		//initialise PrintWriter
		try 
		{
			writer = new PrintWriter(outputFilename);
		} 
		catch (Exception e) 
		{
			System.err.println(e.getMessage());
		}

		
		ArrayList<Patterns.Pattern> patternList = new ArrayList<Patterns.Pattern>();
		ArrayList<String> regexList = new ArrayList<String>();

		//Parse all input strings to patterns and regexes.
		for (int i = 0; i < patternStrings.size(); i++) 
		{
			patternList.add(StringToPattern(patternStrings.get(i)));
			regexList.add(stringToRegex(patternStrings.get(i)));
		}

		//Initialise a membership and a regex solver
		MembershipSolver membershipSolver = new MembershipSolver();
		RegexSolver regexSolver = new RegexSolver();
		double janusTotalSeconds = 0;
		double regexTotalSeconds = 0;
		double totalSeconds = 0;
		double firstTimeStamp;
		double secondTimeStamp = 0;
		double runtimeSeconds;
		Timer timer = new Timer();
		//Set maximum number of seconds a instance is tested
		int maximumTestSeconds = 1200;
		int janusTestsInterrupted = 0;
		int regexTestsInterrupted = 0;
		int averageWordlengthSum = 0;

		//Solve all instances
		for (int i = 0; i < patternStrings.size(); i++) 
		{
			averageWordlengthSum = averageWordlengthSum + words.get(i).length();

			writer.println("Instance number " + (i + 1));
			writer.println(patternStrings.get(i));
			writer.println(words.get(i));
			writer.println("wordlength: " + words.get(i).length());
			writer.println("average wordlength: " + (averageWordlengthSum)
					/ (i + 1));
			writer.println("");

			// -------------------JANUS TEST-------------------\\
			//Solve instance with the Janus automata approach

			writer.println("Solve instance with Janus Automaton:");
			membershipSolver.setInstance(patternList.get(i), words.get(i));
			Interrupt janusInterrupt = new Interrupt(maximumTestSeconds,
					membershipSolver);
			timer = new Timer();
			timer.schedule(janusInterrupt,
					janusInterrupt.getInterruptSeconds() * 1000);

			firstTimeStamp = Calendar.getInstance().getTimeInMillis();

			//Solve the current instance, with the Janus automata approach
			membershipSolver.run();

			secondTimeStamp = Calendar.getInstance().getTimeInMillis();
			timer.cancel();

			runtimeSeconds = ((secondTimeStamp - firstTimeStamp) / 1000);

			if (!janusInterrupt.isInterrupted()) 
			{
				if (membershipSolver.isMember()) 
				{
					writer.println("POSITIVE");
				} 
				else 
				{
					writer.println("NEGATIVE");
				}
				writer.println(runtimeSeconds + " seconds.");
				writer.println("");
				janusTotalSeconds = janusTotalSeconds + runtimeSeconds;
				totalSeconds = totalSeconds + runtimeSeconds;
			} 
			else 
			{
				janusTestsInterrupted++;
				writer.println("Test interrupted");
				writer.println(runtimeSeconds + " seconds.");
				writer.println("");
				janusTotalSeconds = janusTotalSeconds + maximumTestSeconds;
				totalSeconds = totalSeconds + maximumTestSeconds;
			}

			writer.flush();

			// -------------------REGEX TEST-------------------\\
			//Solve instance with the Java regex engine
			
			writer.println("Solve instance with Java Regex Engine:");

			java.util.regex.Pattern regexPattern = java.util.regex.Pattern
					.compile(regexList.get(i));
			Matcher matcher = regexPattern.matcher(words.get(i));

			//Solve the current instance, with the Java regex engine
			regexSolver.setInstance(regexPattern, matcher);
			Thread regexThread = new Thread(regexSolver);

			firstTimeStamp = Calendar.getInstance().getTimeInMillis();

			boolean isInterrupted = false;
			try 
			{
				regexThread.start();

				//do nothing while the thread is active
				while (regexThread.isAlive()
						&& ((Calendar.getInstance().getTimeInMillis() - firstTimeStamp) / 1000) < maximumTestSeconds) 
				{
					
				}

				if (((Calendar.getInstance().getTimeInMillis() - firstTimeStamp) / 1000) >= maximumTestSeconds) 
				{
					regexThread.interrupt();
					isInterrupted = true;
				}

			}
			catch (Exception e) 
			{
				System.out.println(e.getMessage());
			}

			secondTimeStamp = Calendar.getInstance().getTimeInMillis();
			timer.cancel();

			runtimeSeconds = ((secondTimeStamp - firstTimeStamp) / 1000);

			if (!isInterrupted) {
				if (regexSolver.isMember()) {
					writer.println("POSITIVE");
				} else {
					writer.println("NEGATIVE");
				}
				writer.println(runtimeSeconds + " seconds.");
				writer.println("");
				regexTotalSeconds = regexTotalSeconds + runtimeSeconds;
				totalSeconds = totalSeconds + runtimeSeconds;
			} else {
				regexTestsInterrupted++;
				writer.println("Test interrupted");
				writer.println(runtimeSeconds + " seconds.");
				writer.println("");
				regexTotalSeconds = regexTotalSeconds + maximumTestSeconds;
				totalSeconds = totalSeconds + maximumTestSeconds;
			}

			writer.println("Total seconds:" + totalSeconds);
			writer.println("Janus seconds:" + janusTotalSeconds);
			writer.println("Regex seconds:" + regexTotalSeconds);
			writer.println("Janus tests interrupted:" + janusTestsInterrupted);
			writer.println("Regex tests interrupted:" + regexTestsInterrupted);

			writer.println("----------------------------------------");

			writer.flush();

			System.out.println("Instance " + (i + 1) + " solved");
		}
	}

	
	public void readInput() 
	{
		//This function reads the input
		patternStrings = new ArrayList<String>();
		words = new ArrayList<String>();

		String patternString = "";
		String word = "";

		try 
		{
			reader = new BufferedReader(new FileReader(inputFilename));
			patternString = reader.readLine();
			word = reader.readLine();

			//read until end of file ("-") is reached
			while (!patternString.equals("-")) 
			{
				words.add(word);
				patternStrings.add(patternString);
				patternString = reader.readLine();
				word = reader.readLine();
			}
		} 
		catch (Exception ex) 
		{
			System.err.println(ex.getMessage());
		}

	}

	
	public Patterns.Pattern StringToPattern(String patternString) 
	{
		String[] variableStrings = patternString.split(" ");
		ArrayList<Integer> varOrder = new ArrayList<Integer>();

		//parse string into a variables array
		for (int i = 0; i < variableStrings.length; i++) 
		{
			int temp = Integer.parseInt(variableStrings[i]);
			varOrder.add(temp);
		}

		ArrayList<Integer> differentVariables = new ArrayList<Integer>();

		for (int i = 0; i < varOrder.size(); i++) 
		{
			if (!differentVariables.contains(varOrder.get(i))) 
			{
				differentVariables.add(varOrder.get(i));
			}
		}

		int varNumber = differentVariables.size();
		int[] occurrenceVector = new int[varNumber];

		//create occurrence vector
		for (int i = 0; i < varOrder.size(); i++) 
		{
			occurrenceVector[varOrder.get(i)]++;
		}

		//create the pattern
		
		Patterns.Pattern pattern = new Patterns.Pattern();
		pattern.setLength(varOrder.size());
		pattern.setVarNumber(varNumber);
		pattern.setVarOrder(varOrder);
		pattern.setOccurrenceVector(occurrenceVector);

		PatternAnalyser patternAnalyser = new PatternAnalyser();

		//Compute variable distance of the pattern
		pattern.setVarDistance(patternAnalyser.computeVariableDistance(pattern));

		return pattern;
	}

	public String stringToRegex(String patternString) 
	{
		//This function parses the pattern string into a regex
		String[] patternSplitted = patternString.split(" ");
		int[] varOrder = new int[patternSplitted.length];
		ArrayList<Integer> varList = new ArrayList<Integer>();

		for (int i = 0; i < varOrder.length; i++) 
		{
			varOrder[i] = Integer.valueOf(patternSplitted[i]);
			if (!varList.contains(varOrder[i])) 
			{
				varList.add(varOrder[i]);
			}
		}

		int varNumber = varList.size();
		boolean[] variableSeen = new boolean[varNumber];
		for (int i = 0; i < variableSeen.length; i++) 
		{
			variableSeen[i] = false;
		}

		String regex = "";

		for (int i = 0; i < varOrder.length; i++) 
		{
			String regexPart = "";
			if (variableSeen[varOrder[i]] == false) 
			{
				//First occurrences of variables are represented by ([0-9]*?) 
				regexPart = "([0-9]*?)";
				variableSeen[varOrder[i]] = true;
			}
			else 
			{
				//All other occurrences of variables are represented by \i, where i is the index of the variable
				regexPart = "\\" + (varOrder[i] + 1);
			}
			regex = regex + regexPart;
		}

		//The symbols "^, $" force the matching string to start at the first position and end at the last 
		return "^" + regex + "$";
	}

}
